﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

// https://learn.microsoft.com/zh-cn/dotnet/csharp/language-reference/
namespace ConsoleApp
{
    /*
     * .NET Framework（.NET框架）
        .NET是微软公司下的一个开发平台，是.NET程序开发和运行的环境，在这个平台下可以用不同的语言进行开发，因为.NET是跨语言的一个平台。


        1、ASP.NET 只能部署在Window系统平台上，无法跨平台部署；ASP.NET Core 支持跨平台不仅可以部署在Window操作系统上，也能部署在Lunix操作系统上；
        2、ASP.NET属于 大包大揽式的，一旦启动就加载所有的组件，而ASP.NET Core 是需要按需加载，占用资源更少

        下载.NET SDK: https://dotnet.microsoft.com/zh-cn/download/dotnet
        1.生成应用 - SDK

        2.运行应用 - 运行时 Hosting Bundle: HTTP 错误 500.19 - Internal Server Error
        安装成功之后
        WIN+R打开运行窗口
        输入cmd 按回车
        输入dotnet
        查看环境: dotnet --info

        IDE: Visual Studio
        下载地址: https://visualstudio.microsoft.com/zh-hans/
        项目切换环境 右键 -- 属性 -- 目标框架

        IIS 注册 Framework 4.8
        1. 管理员身份运行cmd
        2. 进入Framework 4.8所在目录：cd C:\Windows\Microsoft.NET\Framework64\v4.0.30319
        3. 输入命令：aspnet_regiis.exe -i
     */
    /*
    访问修饰符关键字
    public：公共的，所有对象都可以访问，但需要引用命名空间。
    private：私有的，类的内部才可以访问。
    protected：受保护的，类的内部或类的父类和子类中可以访问。
    internal：内部的，同一个程序集的对象可以访问，程序集就是命名空间。
    protected internal：受保护的内部访问级别，可在同一程序集中或派生类中访问‌12。
    类型声明关键字
    class：声明一个类。
    struct：声明一个值类型结构。
    interface：声明一个接口。
    enum：声明一个枚举。
    delegate：声明一个委托类型。
    record：声明一个记录类型‌13。
    变量和方法修饰符关键字
    static：表示一个静态成员。
    readonly：表示一个只读字段。
    const：表示一个常量字段。
    volatile：表示一个字段可以由多个线程同时访问。
    override：重写基类中的虚方法。
    virtual：声明一个虚方法或属性。
    abstract：声明一个抽象类或方法，需要在派生类中实现。
    sealed：表示一个类不能被继承。
    extern：表示方法在其他地方实现（如DLL中）。
    async 和 await：用于异步编程‌12。
    类型转换关键字
    explicit：显式类型转换运算符。
    implicit：隐式类型转换运算符。
    operator：定义类型转换或运算符重载‌1。
    异常处理关键字
    try：包含可能引发异常的代码块。
    catch：捕获并处理异常。
    finally：无论是否发生异常，都会执行的代码块。
    throw：抛出异常‌12。
    程序流程控制关键字
    if/else/elif：条件语句。
    switch：多路选择语句。
    for/foreach/while/do-while：循环语句。
    break：跳出循环或switch语句。
    continue：跳过当前循环的剩余部分，进入下一次迭代。
    goto：无条件跳转到标签。
    return：从方法中返回‌12。
    转换和操作符关键字
    as：执行引用转换。
    is：检查对象是否与给定类型兼容。
    sizeof：获取类型或变量在内存中的大小。
    default：获取类型的默认值‌12。
    泛型相关关键字
    where：泛型约束，用于指定泛型参数应满足的条件。
    <T>：用于定义泛型类型、方法、委托等‌1。
    其他关键字
    using：引入命名空间或定义资源的使用范围。
    namespace：声明一个命名空间‌
     */

    // 基本语法
    internal class DemoBasic
    {
        // 注释
        public void chapter01()
        {
            // 单行注释 //
            // 多行注释 /*要注释的内容*/ 
            // 文档注释 /// 多用来解释类或者方法
            // #Region和#endRegine——适用于代码多的时候
            // 注释快捷键 ： CTRL + K + C / U
        }

        // 调试输出
        // Console
        // Trace
        // Debug

        // 变量
        public void chapter02()
        {
            // 定义变量
            //var <variable_name> = value;
            //<data_type> <variable_name> = value;
            //只能用于“局部变量”，不能用于字段、方法参数

            var name = "wm";
            var age = 32;
            Console.WriteLine(name + age);

            //动态类型
            //dynamic <variable_name>
            //可以用于类型的字段、方法参数、方法返回值、泛型类型参数

            // ?可选参数
        }

        // 常量
        public void chapter03()
        {
            // 定义常量
            // 1.编译时常量（必须初始化）
            //const <data_type> <constant_name> = value;

            // 2.运行时常量（可以不初始化）
            // readonly 
            Console.WriteLine();
        }

        // 运算符
        public void chapter04()
        {
            // +号的作用
            //1)、连接：当 + 号两边有一边是字符串的时候，+号就起到连接的作用。
            //2)、相加：两边是数字的时候

            // 转义符

            // @符号
            //1、取消\在字符串中的转义作用，使其单纯的表示为一个'\'
            //2、将字符串按照编辑的原格式输出

            //定义多行字符串的方式 在定义的前面加上@符号
            string aa = @"123
4567890";
            Console.WriteLine(aa);
        }

        // 表达式
        // 运算符和操作数（常量、变量、表达式）组成

        // 基本数据类型
        public void chapter05()
        {
            // 1.值类型
            // 1.1 简单 整数,字符,布尔
            // 1.2 复合 结构,枚举
            // uint是一个无符号整数类型，它可以存储非负整数
            uint num = 100;

            // 2.引用类型
            // 2.1 类   string object

            // 2.2 接口

            // 2.3 数组

            // 2.4 委托

            // 3.指针类型
            // 声明: type* identifier;
        }

        // 流程控制
        public void chapter06()
        {
        }

        // 复杂数据类型
        // 1.枚举
        public enum EnumSex
        {
            男 = 1,
            女 = 2,
        }

        // 2.结构
        public struct StructPerson
        {
            //成员;//字段
        }

        // 3.数组
        public void array()
        {
            // 方式一
            int[] arr1; //声明变量
            arr1 = new int[3]; //创建数组对象
            Console.WriteLine("arr[2]=" + arr1[2]); //访问数组中的第三个元素
            Console.WriteLine("数组的长度是：" + arr1.Length); //打印数组长度

            // 方式二
            int[] arr2 = new int[4]; //定义可以存储4个整数的数组
            arr2[0] = 1; //为第1个元素赋值1

            // 方式三
            int[] arr3 = { 1, 2, 3, 4 }; //对数组进行初始化

            //定义二维数组arr并赋值
            int[,] arrs = new int[3, 4] { { 1, 2, 3, 4 }, { 2, 3, 3, 4 }, { 3, 4, 3, 4 } };

            //arrs.GetLength(0) 获取数组长度

            // 数组的长度一旦固定了，就不能再被改变了
        }

        // 值类型:int double char decimal bool enum struct
        public void ValueTest()
        {
            // 浮点数采用 IEEE 754 存储，小数转二进制会出现无限循环，只能存近似值。
            // 两个近似值相加时，误差可能抵消（0.1+0.1）或叠加 (0.1 + 0.2)
            var a = 0.1 + 0.1;
            Console.WriteLine(a);
            var b = 0.1 + 0.2;
            Console.WriteLine(b);
        }

        // 引用类型：string 数组  自定义类 集合 object 接口
        public void ReferenceTest()
        {
            //ArrayList Hashtable
        }

        // 指针类型
        public void PointerTest()
        {
        }

        // 数据类型转换
        // 1.隐性 1 + ""
        // 2.显性 强制 （类型名）表达式 \ ConvetTo: 数值和数值间 \ xxx.Parse: 数值和字符串之间
        //隐性转换
        //1.decimal不能和其他两个浮点型进行隐转
        //2.无符号的可以向有符号的进行隐转，有符号的不能和无符号的进行隐转
        //3.int a = 2; decimal b = a;——整形和字符型的隐转
        //4.char类型和浮点型，整型的隐转

        // 装箱和拆箱
        public void box()
        {
            // 装箱：就是将值类型转换为引用类型。
            // 拆箱：将引用类型转换为值类型。
            // 看两种类型是否发生了装箱或者拆箱，要看，这两种类型是否存在继承关系。

            //int n = 10;
            //object o = n;//装箱
            //int nn = (int)o;//拆箱

            //ArrayList list = new ArrayList();
            //List<int> list = new List<int>();
            ////这个循环发生了100万次装箱操作
            //Stopwatch sw = new Stopwatch();
            ////00:00:02.4370587
            ////00:00:00.2857600
            //sw.Start();
            //for (int i = 0; i < 10000000; i++)
            //{
            //    list.Add(i);
            //}
            //sw.Stop();
            //Console.WriteLine(sw.Elapsed);
            //Console.ReadKey();

            //这个地方没有发生任意类型的装箱或者拆箱
            //string str = "123";
            //int n = Convert.ToInt32(str);

            int n = 10;
            IComparable i = n; //装箱
        }

        // 值传递和引用传递
        // 值类型->引用类型 装箱
        // 值类型<-引用类型 拆箱
        public void RefTest()
        {
            int number = 10;
            Inc(ref number);
            Console.WriteLine(number);
            Console.ReadKey();
        }

        public static void Inc(ref int n)
        {
            n += 10;
        }

        public double Diff(double x, System.Func<double, double> f)
        {
            double h = 0.0000001;
            return (f(x + h) - f(x)) / h;
        }

        public void StringTest()
        {
            //产生一个不会重复的编号
            Console.WriteLine(Guid.NewGuid().ToString());

            // 字符串比较
            // 1) ==
            // 2) Compare
            // 3) CompareTo
            // 4) Equals

            string str11 = "Compare";
            string str22 = "Compare";

            //1.第一种判断方式
            var result1 = str11 == str22; //C#可以直接进行判断
            Console.WriteLine(result1); //输出result1=true
            //2.第二种判断方式
            var result2 = str11.CompareTo(str22);
            Console.WriteLine(result2); //输出result2=0 
            //3.第三种判断方式
            var result3 = string.Compare(str11, str22);
            Console.WriteLine(result3); //输出result3=0    
            //注意：后面两种方式需要注意str11和str22是不是为null的情况
            //4.第四种判断方式
            var result4 = str11.Equals(str22);
            Console.WriteLine(result4); //输出result4=true
            //5.第五种判断方式
            var result5 = string.Equals(str11, str22);
            Console.WriteLine(result5); //输出result5=true

            string str1 = null; // 空对象 
            string str2 = ""; //空字符串  

            //字符串的不可变性
            //string name = "张三";
            //name = "孙全";
            //Console.WriteLine(name);
            //Console.ReadKey();

            //string s1 = "张三";
            //string s2 = "张三";
            //Console.ReadKey();

            //可以讲string类型 看做是char类型的一个只读数组
            string s = "abcdefg";
            s = "bbcdefg";
            // s[0] = 'b';不能这样做  因为是只读的
            //首先将字符串转换为char类型的数组
            char[] chs = s.ToCharArray();
            chs[0] = 'b';
            //将字符数组转换为我们的字符串
            s = new string(chs);
            //既然可以将string看做char类型的只读数组，所以我可以通过下标去访问字符串中的某一个元素
            Console.WriteLine(s[0]);
            Console.WriteLine(s);
            Console.ReadKey();

            // 字符串长度
            Console.WriteLine("长度{0}", s.Length);

            // 字符串位置
            //string str = "今天天天气好晴朗，天天处天好风光";
            //int index = str.IndexOf('哈',2);
            //Console.WriteLine(index);
            //Console.ReadKey();


            //string str = "今天天气好晴朗，处处好风光";
            //int index = str.LastIndexOf('天');
            //Console.WriteLine(index);
            //Console.ReadKey();


            ////LastIndexOf  Substring
            //string path = @"c:\a\b\c苍\d\e苍\f\g\\fd\fd\fdf\d\vfd\苍老师苍.wav";
            //int index = path.LastIndexOf("\\");
            //path = path.Substring(index+1);
            //Console.WriteLine(path);
            //Console.ReadKey();

            // 格式化字符串
            string.Format("{0} + {1} = {2}", 1, 2, 1 + 2);
            // 格式符
            100.ToString("C");

            // 截取字符串
            //"".Substring();// 长度超出会抛异常 ArgumentOutOfRangeException

            // 分割字符串 Split
            string s1 = "a b   dfd _   +    =  ,,, fdf ";
            char[] chs1 = { ' ', '_', '+', '=', ',' };
            string[] str = s1.Split(chs1, StringSplitOptions.RemoveEmptyEntries);
            Console.ReadKey();

            // 插入字符串
            //"".Insert();

            // 删除字符串
            //"".Remove();

            // 复制字符串
            //"".Copy();
            //"".CopyTo();

            // 替换字符串 Replace
            //string str = "国家关键人物老赵";
            //if (str.Contains("老赵"))
            //{
            //    str = str.Replace("老赵", "**");
            //}
            //Console.WriteLine(str);
            //Console.ReadKey();

            // 可变字符串类
            // StringBuilder()

            // 分割字符串数组
            //把{“诸葛亮”,”鸟叔”,”卡卡西”,”卡哇伊”}变成诸葛亮|鸟叔|卡卡西|卡哇伊,然后再把|切割掉
            string[] names = { "诸葛亮", "鸟叔", "卡卡西", "卡哇伊" };
            string str3 = string.Join("|", names);
            string[] strNew = str3.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            Console.WriteLine(strNew);
            Console.ReadKey();
        }

        public void StringTest1()
        {
            StringBuilder sb = new StringBuilder();
            string str = null;
            //创建了一个计时器，用来记录程序运行的时间
            //00:00:00.0422166
            Stopwatch sw = new Stopwatch();
            sw.Start(); //开始计时
            for (int i = 0; i < 100000; i++)
            {
                //str += i;
                sb.Append(i);
            }

            sw.Stop(); //结束计时
            Console.WriteLine(sb.ToString());
            Console.WriteLine(sw.Elapsed);
            Console.ReadKey();
        }

        public void StringTest2()
        {
            // 验证字符串是否全数字
            var str = "123456";
            bool res = str.All(char.IsDigit);

            //Console.WriteLine("请输入你喜欢的课程");
            //string lessonOne = Console.ReadLine();
            ////将字符串转换成大写
            ////  lessonOne = lessonOne.ToUpper();
            ////将字符串转换成小写形式
            //// lessonOne = lessonOne.ToLower();
            //Console.WriteLine("请输入你喜欢的课程");
            //string lessonTwo = Console.ReadLine();
            ////   lessonTwo = lessonTwo.ToUpper();
            ////   lessonTwo = lessonTwo.ToLower();
            //if (lessonOne.Equals(lessonTwo,StringComparison.OrdinalIgnoreCase))
            //{
            //    Console.WriteLine("你们俩喜欢的课程相同");
            //}
            //else
            //{
            //    Console.WriteLine("你们俩喜欢的课程不同");
            //}
            //Console.ReadKey();

            //练习：从日期字符串（"2008-08-08"）中分析出年、月、日；2008年08月08日。
            //让用户输入一个日期格式如:2008-01-02,你输出你输入的日期为2008年1月2日

            //string s = "2008-08-08";
            ////  char[] chs = { '-' };
            //string[] date = s.Split(new char[] { '-' }, StringSplitOptions.RemoveEmptyEntries);
            //Console.WriteLine("{0}年{1}月{2}日", date[0], date[1], date[2]);
            //Console.ReadKey();
            //老赵


            //Substring 截取字符串

            //string str = "今天天气好晴朗，处处好风光";
            //str = str.Substring(1,2);
            //Console.WriteLine(str);
            //Console.ReadKey();

            //string str = "今天天气好晴朗，处处好风光";
            //if (str.EndsWith("风"))
            //{
            //    Console.WriteLine("是的");
            //}
            //else
            //{
            //    Console.WriteLine("不是的");
            //}
            //Console.ReadKey();


            // string str = "            hahahah          ";
            //// str = str.Trim();
            // //str = str.TrimStart();
            // str = str.TrimEnd();
            // Console.Write(str);
            // Console.ReadKey();

            //string str = "fdsfdsfds";
            //if (string.IsNullOrEmpty(str))
            //{
            //    Console.WriteLine("是的");
            //}
            //else
            //{
            //    Console.WriteLine("不是");
            //}
            //string[] names = { "张三", "李四", "王五", "赵六", "田七" };
            ////张三|李四|王五|赵六|田七
            //string strNew = string.Join("|", "张三","李四","王五","赵六","田七");
            //Console.WriteLine(strNew);
            //Console.ReadKey();

            //"hello c sharp"→"sharp c hello"
            //string str = "hello c sharp";
            //string[] strNew = str.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
            //for (int i = 0; i < strNew.Length / 2; i++)
            //{
            //    string temp = strNew[i];
            //    strNew[i] = strNew[strNew.Length - 1 - i];
            //    strNew[strNew.Length - 1 - i] = temp;
            //}

            //str = string.Join(" ", strNew);
            //Console.WriteLine(str);
            //string.join:将字符串按照指定的分隔符连接
            //Console.WriteLine(strNew);//sharp c hello
            // sharp c hello

            //for (int i = 0; i < strNew.Length; i++)
            //{
            //    Console.WriteLine(strNew[i]);
            //}


            //课上练习3：从Email中提取出用户名和域名：abc@163.com。
            //string email = "285014478@qq.com";
            //int index = email.IndexOf('@');
            //string userName = email.Substring(0, index);
            //string yuMing = email.Substring(index+1);
            //Console.WriteLine(yuMing);
            //Console.WriteLine(userName);
            //Console.ReadKey();
        }

        public void arrays()
        {
            // 数组基类：Array
            //集合 和 数组 的 区别 ： 数组 的长度是固定的。 集合 的长度是可变的。 数组 中存储的是同一类型的元素，可以存储基本数据类型值。 集合 存储的都是对象。 而且对象的类型可以不一致。

            // 创建示例数据
            string[] headers = { "Name", "Age", "Email" };
            string[][] rows =
            {
                new[] { "John Doe", "30", "john.doe@example.com" },
                new[] { "Jane Smith", "25", "jane.smith@example.com" },
                new[] { "Bob Johnson", "35", "bob.johnson@example.com" }
            };


            String B = "12345";
            char[] A = B.ToCharArray();
            Array.Reverse(A);
            String C = new string(A);
        }


        //ref,out,params
        //ref和out的作用: 使传递的参数变成，你变我也变的引用类型变量
        //ref：实参需要初始化赋值
        //out：实参不需要初始化赋值（初始化不初始化都可以）
        //ref有进有出，out只出不进。ref 关键字使参数按引用传递（给）。out 关键字会导致参数通过引用来传递（拿）。
        //相同：都是按地址传递；使用后都将改变原来参数的值；编译时处理方式相同；属性不能作为参数传递。
        //不同：ref 将参数值传进方法， out 无法将参数值传入方法；ref 传入参数前必须先初始化， out 不必在参数传输方法前初始化，但必须在方法中初始化；ref 用在需要被调用的方法修改调用者的引用的时候， out 用在需要返回多个结果的地方。

        //params（变长参数）：以伪数组的形式给多个相同类型的形参进行传递，减少了代码的复杂度
        //params使用规则: 必须是最后一个参数,只能用于一维数组,不能与ref和out一起使用,可以传递零个或多个参数

        public void UpdateValue(ref int value)
        {
            value += 10;
        }

        public void GetValue(out int value)
        {
            value = 10;
        }

        public void PrintValue(in int value)
        {
            Console.WriteLine(value); // value = 11; // 编译错误，因为 in 修饰符不允许修改
        }

        // 循环
        public IEnumerable<int> GetNumbers(int max)
        {
            var numbers = new List<int>();
            for (var i = 1; i <= max; i++)
            {
                numbers.Add(i);
            }

            return numbers;
        }
        
        // yield return
        public IEnumerable<int> GetNumbersByYield(int max)  
        {  
            for (var i = 1; i <= max; i++)  
            {  
                yield return i;  
            }  
        }  
        
        public IEnumerable<string> GetLongWords(string[] words)
        {
            return words.Where(word => word.Length > 5);
        } 
    }
}